
#include "utilsDeformation.h"
#include "quaternion.h"

#include "meshdef/rbfMeshDef.h"
#include "meshdef/clustering.h"
#include "meshdef/skinning.h"

#include "core/tconstraints.h"
#include "meshdef/mlsMeshDeformer.h"
#include "meshdef/mlsSkelMeshDeformer.h"
#include "meshdef/mlsBlenSkelMeshDeformer.h"

#include "meshdef/affineDeformer.h"

typedef TConstraints<Point3>                 PConstraints;
typedef TConstraints<Segment3>               ConstraintsS;
typedef MlsMeshDeformer<PConstraints>        MLSMeshDeformer;
typedef MlsMeshDeformer<ConstraintsS>        MLSMeshDeformerS;
typedef MlsSkelMeshDeformer<PConstraints>    MLSskelMeshDeformer;
typedef MlsSkelMeshDeformer<ConstraintsS>    MLSskelMeshDeformerS;
typedef MlsBlenSkelMeshDeformer<PConstraints> MLS;

// gera uma lista de pontos a partir dos joints do esqueleto
void genPositionsList(GSkeleton *s, vector<Point3> &c);
// atualiza a geometria da mesh usando as posicoes do meshO
void updateMeshPositions(Mesh *meshO, Mesh *mesh);

void deformMesh_MLS_controlPoints(Mesh *meshO, Mesh *mesh, GSkeleton *sO, GSkeleton *sD) {
   
   updateMeshPositions(meshO, mesh);
   MLSskelMeshDeformer mlsctrlpoint(sO, mesh);
   mlsctrlpoint.execute(sD);
}

void deformMesh_MLS_controlSegments(Mesh *meshO, Mesh *mesh, GSkeleton *sO, GSkeleton *sD) {
   
   updateMeshPositions(meshO, mesh);
   MLSskelMeshDeformerS mlsctrlsegments(sO, mesh);
   mlsctrlsegments.execute(sD);
}

void deformMesh_MLS_blending(Mesh *meshO, Mesh *mesh, GSkeleton *sO, GSkeleton *sD) {
   
   cout<<"MLS blending deformation ... begin"<<endl;
   updateMeshPositions(meshO, mesh);
   
   assert(sO->nJoints() == sD->nJoints());
   TConstraints<Point3> O, D;
   for (int i=0; i<sO->nJoints(); ++i) {
      O.push_back(sO->getJointPos(i), i);
      D.push_back(sD->getJointPos(i), i);
   }
   
   MLS mlsblending(sO, O, mesh);
   mlsblending.execute(D);
   //~ cout<<"deforming end ..."<<endl;
}

void deformMesh_MLS_blending(Mesh *meshO, Mesh *mesh, GSkeleton *sO, GSkeleton *sD, 
                             TConstraints<Point3> &O, TConstraints<Point3> &D) {
   
   cout<<"MLS blending deformation ... begin"<<endl;
   updateMeshPositions(meshO, mesh);
   for (int i=0; i<sO->nJoints(); ++i)
      sD->setJointPos(i, sO->getJointPos(i));
   
   MLS mlsblending(sD, O, mesh);
   mlsblending.execute(D);
   cout<<"MLS blending deformation ... done!"<<endl;
}

void deformMesh_RBF_classic(Mesh *meshOri, Mesh *mesh, GSkeleton *sO, GSkeleton *sD) {
   
   updateMeshPositions(meshOri, mesh);
   
   vector<Point3> source, target;
   genPositionsList(sO, source);
   genPositionsList(sD, target);
   
   RbfMeshDef deformer(meshOri);
   deformer.setting(source);
   deformer.setMesh(mesh);
   deformer.execute_deformation(target);
}

void deformMesh_RBF_geodesic(Mesh *meshOri, Mesh *mesh, GSkeleton *sO, GSkeleton *sD) {
   
   updateMeshPositions(meshOri, mesh);
   RbfMeshDef deformer(mesh, sO);
   deformer.execute_deformation(sD);
}

void genPositionsList(GSkeleton *s, vector<Point3> &c) {
   
   for (int i=0; i<s->nJoints(); ++i)
      c.push_back(s->getJointPos(i));
}

void updateMeshPositions(Mesh *meshO, Mesh *mesh) {
   
   Vertex_iterator vio = meshO->vertices_begin();
   Vertex_iterator vi = mesh->vertices_begin();
   while(vio != meshO->vertices_end()) {
      vi->point() = vio->point();
      vi->normal() = vio->normal();
      ++vio; ++vi;
   }
}

/// ///

void deformMesh_AT(Mesh *meshOri, Mesh *mesh, GSkeleton *sO, GSkeleton *sD) {
   
   updateMeshPositions(meshOri, mesh);
   
   cout<<"deformMesh_AT ... inicio"<<endl;
   AffineDeformer deformer(sO, sD);
   deformer.execute(mesh);
   cout<<"deformMesh_AT ... fin"<<endl;
}

void deformMesh_MLS_controlPoints(Mesh *meshOri, Mesh *mesh, TConstraints<Point3> &O, TConstraints<Point3> &D) {
   
   updateMeshPositions(meshOri, mesh);
   MLSMeshDeformer deformer(mesh, O);
   deformer.execute(D);
}

void deformMesh_RBF_classic(Mesh *meshOri, Mesh *mesh, TConstraints<Point3> &O, TConstraints<Point3> &D) {
   
   updateMeshPositions(meshOri, mesh);
   RbfMeshDef deformer(mesh, O.positions);
   deformer.execute_deformation(D.positions);
}
